package demo2

// 工厂方法模式 factorymethod
// 工厂方法模式 Operator
// 工厂方法模式使用子类的方式延迟生成对象到子类中实现。
// Go中不存在继承 所以使用匿名组合来实现
// 看看工厂方法模式的五大要素：
// 模式名称：工厂方法模式
// 目的（What）：定义一个工厂接口创建对象，不同的子类实现这个接口创建各自的对象，创建对象的逻辑由子类实现
// 解决的问题（Why）：当我们明确知道不同地条件下需要创建不同的对象时，使用这种模式（与简单工厂模式解决的问题类似，同时解决了简单工厂模式的部分缺点）
// 解决方案（How）：实现一个工厂接口，包含了New()方法，返回该工厂具体生产的工厂类，不同的子工厂都实现了这个New()方法，实现了工厂接口，这样就让具体工厂类的创建由各自对应的工厂类工厂实现
// 解决效果：
// 优点：
// 在添加新的工厂类时，不需要更改工厂类逻辑，而是添加新的子工厂类
// 屏蔽产品的具体实现，调用者只关心产品的接口
// 缺点：
// 每次需要新添加一个工厂时，比如现在是玩具厂，需要添加一个针织厂，就需要新建一个工厂对象和一些工厂类对象，使得系统中类的个数成倍在增加，在一定程度上增加了系统的复杂度（这个缺点其实还是存在）

// 工厂的第二种设计模式
import "fmt"

type Operator interface {
	SetA(int)
	SetB(int)
	Result()int
}

type OperatorFactory interface{
	Create() Operator
}
// 流水线上工厂操作的重复的方法模板
type OperatorBase struct{
	a,b int
}

func (o *OperatorBase)SetA(a int) {
	o.a = a
}
func (o *OperatorBase)SetB(b int){
	o.b = b
}

// 可以拓展工厂，
// 不同的工厂生产不同的对象

type MinOperatorFactory struct{}

func (*MinOperatorFactory)Create() Operator{
	return &MinOperator{
		OperatorBase:&OperatorBase{},
	}
}
// 工厂生产对象的实例以及实例对应的方法
type MinOperator struct{
	*OperatorBase
}

func (m *MinOperator)Result() int {
	return m.a + m.b
}
// --------------------------------场景2-----------------------------------
// 需求: 有一个汽车生产工厂， 生产汽车， 汽车需要的构造为 发动机，轮胎，油门.....等等
// 需求分析: 需要一个CarFactory工厂来生产汽车,
// 汽车构造是:发动机。轮胎，油门等等
// 每个汽车有自己的方法和相同的方法，相同的方法就是生产出来都能Run 跑不同的方法可能颜色不一样等等

type Car interface{
	Enginer() // 发动机
	Throttle() // 油门
	Tyre() // 轮胎

	// 也可以添加一个组合的方法
	Set(string,string)
}

type CarFactory interface{
	CreateCar() Car
}

// 设计图纸
type carbase struct{
	name, color string
}
func (c *carbase)Enginer(){
	fmt.Printf("%s--的-%s:发动机制造中....\n",c.color,c.name)
}
func (c *carbase)Throttle(){
	fmt.Printf("%s--的-%s:油门制造中....\n",c.color,c.name)
}
func (c *carbase)Tyre(){
	fmt.Printf("%s--的-%s:轮胎制造中....\n",c.color,c.name)
}

// 定义一个工厂外部暴露的
type MadeCarFactory struct{}

func (m *MadeCarFactory)CreateCar() Car{
	return &madeCar{
		carbase:&carbase{},
	}
}
// 定义一个car 里面包含的继承的方法
// 当然也可以不选择继承根据场景
type madeCar struct{
	*carbase
}
// 如果模板的方法不满足的话，这里可以写自己的方法。类似继承的关系
func (m *madeCar) Set(name,color string){
	m.name = name
	m.color = color
}

// --------------------------------场景3-----------------------------------
// 需求：用户下单选择
// 需求分析：用户下单的时候，提供的支付方式:支付宝,vx,和银行卡等等都可以完成支付
// 需要一个ManagerPayment的管理可以创建不同的支付方式，可以拓展多个支付方式，当然也开始使用策略设计模式
// 所有的支付方式：动作Pay(),refund()退款,transfer()转账,Query()查询余额功能

type paymentMethod interface{
	Pay(float64)
	Refund(float64)
	Transfer(float64)
	Query() float64
	// 充值余额
	Set(float64)
}

type paymentFactory interface{
	CreatePay() paymentMethod
}

type paymentBase struct{
	amount float64
}

func (p *paymentBase)Pay(money float64){
	if p.amount < money {
		fmt.Println("余额不足....")
		return
	}
	p.amount -= money
}
func (p *paymentBase)Refund(money float64){
	p.amount += money
}
func (p *paymentBase)Transfer(money float64){
	if p.amount < money {
		fmt.Println("转账余额不足....")
		return
	}
	p.amount -= money
}

// 工厂生产不同的支付方式
type PaymentMethodFactory struct{}
func (*PaymentMethodFactory) CreatePay() paymentMethod{
	return &paymentVX{
		paymentBase:&paymentBase{},
	}
}

// Vx 支持的方法
type paymentVX struct{
	*paymentBase
}
func (p *paymentVX)Set(money float64){
	// 配置限额
	p.amount = money
}

func (p *paymentVX)Query() float64{
	return p.amount
}

// --------------------------------场景4-----------------------------------

// 需求: 实现用户状态保持，由于产品经理的要求 我们要用到Cookie 和Session 或者Jwt等
// 需求分析:不管使用来实现状态保持，那么实现状态保持的数据需要存储，我们可以选择内存，或者NoSQL【这里用redis】
// 存储的话分析:Set,Get,Expire(time.Second),Delete(),
// 提供选择的存储模式对象有redis,memory 内存等。..

type storage interface{
	Set(name,value string)
	Expire(float64) // 过期时间这里就简单描述实现了，不用一些策略，自己可以实现
	Delete(name string)

	Get(name string)
}

type storageManager interface{
	CreateStorage() storage
}

type storageBase struct{
	data map[string]string // 这里使用的是map 当然也开始使用[]map[string]string 来都行
}

func (s *storageBase)Set(name,value string){
	s.data  = make(map[string]string)
	s.data[name] = value
}

func (*storageBase)Expire(time float64){
	fmt.Printf("设置%f 时间过期\n",time)
}
func (s *storageBase)Delete(name string){
	delete(s.data,name)
}

type StorageFacotry struct{}

func (*StorageFacotry)CreateStorage() storage{
	return &memory{
		storageBase:&storageBase{}, // 这里工厂生产的并非指定的，可以根据接收参数不同生产不同的实例，
	}
}

type memory struct{
	*storageBase
}

func (m *memory)Get(name string){
	if _,ok := m.data[name];ok{
		fmt.Println("get data :",m.data[name])
		return
	}
	fmt.Println("get data 不存在")
}